package com.lottery.material.config.redislock;

import com.lottery.material.jedis.JedisClient;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.util.Objects;

/**
 * Description：redis分布式锁
 * Created by 朱正磊 on 2019-05-11 21:24
 */
@Component
@Slf4j
@SuppressWarnings("ALL")
public class RedisLock {

    @Autowired
    @Qualifier("jedisClientCluster")
    private JedisClient jedisClient;

    /**
     * description：加锁
     * user 朱正磊
     * time 2019-05-11 21:28
     * @param key 键
     * @param value 值：当前时间 + 超时时间
     * @return 如果加锁成功返回true，否则返回false
     */
    public boolean lock(String key, String value) {
        //判断是否已经有线程加了锁，如果没有加锁，那么进行加锁，同时返回true，否则进行下一步
        if (Objects.equals(jedisClient.setnx(key, value), 1L)) {
            return true;//加锁成功，放行业务逻辑代码
        }
        //利用超时时间解决死锁的问题
        //假如当前线程currentValue=A，这时同时来了两个线程，这两个线程的value都是B
        String currentValue = jedisClient.get(key);
        //如果锁已经过期（currentValue小于当前时间）
        if (!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()) {
            //获取上一个锁，同时加新的锁
            //getAndSet方法一次只允许一个线程去执行，多线程执行该方法存在先后顺序，即体现了redis的单线程原理
            //也就是说多线程的情况下也只会有一个线程拿到锁
            String oldValue = jedisClient.getSet(key, value);
            //第一个线程进来的时候oldValue=currentValue(A=A)，第二个线程进来的时候oldValue!=currentValue(B!=A)
            if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
                return true;//加锁成功，放行业务逻辑代码
            }
        }
        return false;//加锁失败，不放行业务逻辑代码
    }

    /**
     * description：解锁
     * user 朱正磊
     * time 2019-05-11 22:38
     * @param key 键
     * @param value 值
     */
    public void unlock(String key, String value) {
        try {
            String currentValue = jedisClient.get(key);//获取当前锁
            if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {//如果入参就是当前锁
                jedisClient.del(key);//删除当前锁，即进行解锁
            }
        } catch (Exception e) {
            log.error("【redis分布式锁】解锁异常，{}", e);
        }
    }

}
